using UnityEngine;
using System.Collections;

using UnityEngine.UI;
using UnityEngine.EventSystems;

using Game.UI.Component;
using System;
using Game.Global.Helper;

namespace Game.UI
{
    /// <summary>
    /// 提供各种UI操作中常用的函数
    /// </summary>
	public static class GameUIFuncs
    {
        /// <summary>
        /// 将UI挂载到目标物体上，且符合其缩放规则
        /// </summary>
        public static void AttachRectTrans(Transform srcRectTrans, Transform toTarget)
        {
            srcRectTrans.SetParent(toTarget, false);
        }

        /// <summary>
        /// 将UI挂载到目标物体上，且符合其缩放规则
        /// </summary>
        public static void AttachTo(this Transform src, Transform toTarget)
        {
            src.SetParent(toTarget, false);
        }

        #region UI资源创建
        /// <summary>
        /// 创建一个UI物体
        /// </summary>
        public static RectTransform CreateUIObj(string objName)
        {
            GameObject go = new GameObject();
            go.name = objName;
            return go.AddComponent<RectTransform>();
        }

        /// <summary>
        /// 从Texture2D中创建Sprite
        /// </summary>
        public static Sprite ToSprite(this Texture2D tx, float pivotX = 0.5f, float pivotY = 0.5f, float pixelsPerUnit = 100)
        {
            //创建一个Sprite
            Sprite sp = Sprite.Create(tx, new Rect(0, 0, tx.width, tx.height), new Vector2(pivotX, pivotY), pixelsPerUnit);

            return sp;
        }

        /// <summary>
        /// 创建一个空白的Panel
        /// </summary>
        public static RectTransform CreatePanelPlain(string objName = "panel")
        {
            return CreateUIObj(objName);
        }

        /// <summary>
        /// 创建一个Panel（挂载了背景Img）
        /// </summary>
        public static RectTransform CreatePanel(string objName, float alpha = 0.5F, float r = 1F, float g = 1F, float b = 1F)
        {
            Image img = CreateImage(objName, GetImg(GetImageType.UISprite));

            //有不透明度的设置需求
            Color col = new Color(r, g, b, alpha);

            img.color = col;

            return img.rectTransform;
        }

        public static Text CreateText(string strText, RectTransform attachToParent = null, TextAnchor alignType = TextAnchor.MiddleLeft, bool isBestFit = true)
        {
            RectTransform rectTrans = CreateUIObj("Text");
            Text text = rectTrans.gameObject.AddComponent<Text>();
            text.text = strText;
            text.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
            text.resizeTextMaxSize = 30;

            //Attach if need:
            if (attachToParent != null)
            {
                AttachRectTrans(rectTrans, attachToParent);
            }

            //--默认自动大小
            text.resizeTextForBestFit = isBestFit;
            //--设置字体颜色大小
            text.color = Color.black;

            //----Font布局：
            text.alignment = alignType;
            return text;
        }

        /// <summary>
        /// 创建Text，同时指定位置和Size
        /// </summary>
        public static Text CreateText(string strText, Vector2 percentPos, Vector2 percentSize, RectTransform attachToParent = null, TextAnchor alignType = TextAnchor.MiddleLeft, bool isBestFit = true)
        {
            Text txt = CreateText(strText, attachToParent, alignType, isBestFit);
            SetRectTransSize(txt.rectTransform, percentPos, percentSize);
            return txt;
        }

        public static Image CreateImage()
        {
            return CreateImage("Image", GetImg(GetImageType.UISprite));
        }

        public static Image CreateImage(Sprite sp, bool fullFillParent = false, Transform attachToTransform = null)
        {
            return CreateImage("Image", sp, fullFillParent, attachToTransform);
        }

        public static Image CreateImage(string objName)
        {
            return CreateImage("Image", GetImg(GetImageType.UISprite));
        }

        public static Image CreateImage(string objName, Sprite sprite,
             bool fullFillParent = false, Transform attachToTransform = null, Image.Type imgType = Image.Type.Sliced)
        {
            RectTransform innerContent = GameUIFuncs.CreateUIObj(objName);
            Image img = innerContent.gameObject.AddComponent<Image>();
            img.sprite = sprite;
            img.type = imgType;

            if (fullFillParent)
            {
                SetRectTransFullFilled(innerContent);
            }

            if (attachToTransform != null)
            {
                AttachRectTrans(innerContent, attachToTransform);
            }
            return img;
        }

        /// <summary>
        /// 创建一个不可交互的Slider
        /// </summary>
        /// <param name="sliderName"></param>
        /// <returns></returns>
        public static Slider CreateSlider(string sliderName = "Slider")
        {
            return CreateSlider(sliderName, Color.white, Color.white);
        }

        /// <summary>
        /// 创建一个滑条，但是默认不推荐用于创建可交互滑条(该slider交互不美观)
        /// </summary>
        public static Slider CreateSlider(string sliderName, Color backgroundColor, Color foregroundColor, int sliderWidth = 128, int sliderHeight = 16, bool isControllable = false, bool addOutLineEffect = true)
        {
            //1.总体Parent
            RectTransform rect_sliderParent = CreateUIObj(sliderName);
            //--设置总体大小：（坐标系大小）
            SetRectTransSize(rect_sliderParent, 0, 0, sliderWidth, sliderHeight, BaseAnchorType.CenterVertHorz);
            //--attach生成物体，并生成Slider
            Slider sliderParent = rect_sliderParent.gameObject.AddComponent<Slider>();

            //2.创建背景，也就是Slider背景槽
            Image imgBackground = CreateImage("background", GetImg(GetImageType.UIBackground),
                                    true);                                       //并将之FullFill 

            //--设置对应的背景槽颜色
            imgBackground.color = backgroundColor;

            //--Attach to parent
            AttachRectTrans(imgBackground.rectTransform, rect_sliderParent);


            //3.前景槽，也就是Slider具体值显示部分
            //--前景槽父物体：
            RectTransform rect_fillArea = CreateUIObj("FillArea");
            SetRectTransFullFilled(rect_fillArea);
            AttachRectTrans(rect_fillArea, rect_sliderParent);

            //--前景槽子物体：图片创建
            Image imgForeground = CreateImage(GetImg(GetImageType.UISprite),
                                            true,                               //FullFill 父物体
                                            rect_fillArea);                     //attach到该物体上
            imgForeground.color = foregroundColor;

            //--slider的填充区域指定：
            sliderParent.fillRect = rect_fillArea;

            //4.Handler，也就是Slider的滑块
            //--可控制滑块时才创建Handler：
            if (isControllable)
            {
                //--创建Handler的父物体:
                RectTransform handlerParent = CreateUIObj("Handler");
                SetRectTransFullFilled(handlerParent);

                //--获得子物体中的Image,并挂载入父物体：
                Image imgHandler = CreateImage("Handler", GetImg(GetImageType.Knob), false, handlerParent, Image.Type.Simple);

                //--计算handler控制块的大小
                int handlerSize = (int)(sliderHeight * 1.2F);
                //--Handler大小改变：
                SetRectTransSize(imgHandler.rectTransform, 0, 0, handlerSize, handlerSize, BaseAnchorType.CenterVertHorz);

                //--将handlerParentAttach回Slider
                AttachRectTrans(handlerParent.transform, rect_sliderParent);

                //--slider设置：
                sliderParent.handleRect = imgHandler.rectTransform;
                //--变色属性
                sliderParent.targetGraphic = imgHandler;

            }
            else
            {
                //--无滑块变色：
                sliderParent.transition = Selectable.Transition.None;
            }

            //--是否可交互，可控制
            sliderParent.interactable = isControllable;

            //5.其他UI效果
            if (addOutLineEffect)
            {
                AddEffect(imgBackground.gameObject, UIEffectType.OutLine);

            }
            return sliderParent;
        }

        public static Button CreateButton(string btnText, CallbackFunc onClick = null)
        {
            return CreateButton(null, btnText, onClick);
        }

        public static Button CreateButton(Sprite btnSprite = null, string strBtnText = null, CallbackFunc cbOnClick = null)
        {
            //--创建Obj
            RectTransform buttonTrans = CreateImage("Btn_Yes", GetImg(GetImageType.Button)).rectTransform;

            //--加入Button
            Button button = buttonTrans.gameObject.AddComponent<Button>();

            //--设置回调
            if (cbOnClick != null)
            {
                button.onClick.AddListener(() =>
                {
                    cbOnClick();
                });
            }

            //--如果有另外的Img，则为Button添加Img，否则添加Text文字
            #region 如果有另外的Img，则为Button添加Img，否则添加Text文字

            RectTransform innerContent;
            if (btnSprite != null)
            {
                //Add img obj:
                innerContent = CreateImage(btnSprite).rectTransform;
            }
            else
            {
                //Add text obj:
                innerContent = CreateText(strBtnText, null, TextAnchor.MiddleCenter).rectTransform;
            }

            //--设置填满button
            GameUIFuncs.SetRectTransFullFilled(innerContent);
            //Attach to btn:
            GameUIFuncs.AttachRectTrans(innerContent, buttonTrans);

            #endregion

            return button;
        }

        /// <summary>
        /// 设置Recttrans，令之填满画布
        /// </summary>
        public static void SetRectTransFullFilled(RectTransform rectTrans)
        {
            rectTrans.anchorMin = Vector2.zero;
            rectTrans.anchorMax = Vector2.one;
            rectTrans.position = Vector2.zero;

            //Anchors在同一点的时候，sizeDelta相当于设置长宽，但是Anchors不在同一点时，表示的只是比Anchors矩形大或者小多少。
            rectTrans.sizeDelta = Vector2.zero;
        }

        /// <summary>
        /// 设置UI的位置、绝对大小，（以左下角为锚点）
        /// </summary>
        /// <param name="rectTrans"></param>
        /// <param name="position"></param>
        /// <param name="size"></param>
        public static void SetRectTransSizeAbsolute(RectTransform rectTrans, Vector2 position, Vector2 size)
        {
            //--锚点为左下角：
            rectTrans.anchorMin = rectTrans.anchorMax = Vector2.zero;
            rectTrans.pivot = new Vector2(0, 0);

            //--设置位置：
            rectTrans.anchoredPosition = position;

            //--设置大小
            rectTrans.sizeDelta = size;
        }

        /// <summary>
        /// 设置UI的位置，相对大小（占屏幕的百分比为单位）
        /// </summary>
        /// <param name="percentPos">位置，相对屏幕的位置</param>
        /// <param name="percentSize">大小，相对屏幕的大小</param>
        public static void SetRectTransSize(RectTransform rectTrans, Vector2 percentPos, Vector2 percentSize)
        {
            SetRectTransSize(rectTrans, percentPos.x, percentPos.y, percentSize.x, percentSize.y);
        }

        public enum BaseAnchorType
        {
            /// <summary>
            /// 以UI的中心点作为锚点
            /// </summary>
            CenterVertHorz,

            /// <summary>
            /// 基准点以Fill四个边角为准，填充父物体
            /// </summary>
            FillCorner,
        };
        /// <summary>
        /// 设置UI的位置，大小（占屏幕的百分比为单位）
        /// </summary>
        /// <param name="percentPosX">位置，相对屏幕的位置</param>
        /// <param name="percentSizeX">大小，相对屏幕的大小</param>
        /// <param name="anchorType">参考大小类型</param>
        public static void SetRectTransSize(RectTransform rectTrans, float percentPosX, float percentPosY,
                                                                                                     float percentSizeX, float percentSizeY,
                                                                BaseAnchorType anchorType = BaseAnchorType.FillCorner)
        {
            //锚点位置计算：
            #region 锚点位置计算：
            switch (anchorType)
            {
                //    //--设置锚点为左下角
                //    rectTrans.anchorMin = Vector2.zero;
                //    rectTrans.anchorMax = Vector2.zero;
                //    rectTrans.pivot = Vector2.zero;

                //    Vector2 pos = Vector2.zero, size = Vector2.zero;

                //    //--获取画布大小：
                //    Vector2 parentSize = DialogCanvas.canvasResolution;
                //    float scrWidth = parentSize.x, scrHeight = parentSize.y;


                //    //--计算位置：
                //    pos.x = scrWidth * percentPosX;
                //    pos.y = scrHeight * percentPosY;
                //    //--计算大小：
                //    size.x = scrWidth * percentSizeX;
                //    size.y = scrHeight * percentSizeY;

                //    //--设置位置、大小
                //    rectTrans.position = pos;
                //    rectTrans.sizeDelta = size;
                //    break;

                case BaseAnchorType.CenterVertHorz:
                    //--锚点为中心点
                    rectTrans.anchorMin = new Vector2(0.5F, 0.5F);
                    rectTrans.anchorMax = new Vector2(0.5F, 0.5F);
                    rectTrans.pivot = new Vector2(0.5F, 0.5F);

                    //--Size
                    rectTrans.sizeDelta = new Vector2(percentSizeX, percentSizeY);
                    //--Position
                    rectTrans.position = new Vector3(percentPosX, percentPosY, 0);
                    break;

                case BaseAnchorType.FillCorner:
                    //锚点为填充父物体型的
                    rectTrans.anchorMin = new Vector2(percentPosX, percentPosY);
                    rectTrans.anchorMax = new Vector2(percentPosX + percentSizeX, percentPosY + percentSizeY);
                    rectTrans.pivot = Vector2.zero;

                    //--SizeDelta保持为0即可：
                    rectTrans.sizeDelta = Vector2.zero;
                    break;
            }
            #endregion

        }

        ///<summary>加上相对位移的位置设置</summary>
        /// <param name="deltaLeft"></param>
        /// <param name="deltaRight"></param>
        /// <param name="deltaUp"></param>
        /// <param name="deltaDown"></param>
        public static void SetRectTransSize(RectTransform rectTrans, float percentPosX, float percentPosY,
                                                                                                     float percentSizeX, float percentSizeY,
                                                                                                     int deltaLeft, int deltaRight,
                                                                                                     int deltaUp, int deltaDown,
                                                                BaseAnchorType anchorType = BaseAnchorType.FillCorner)
        {
            #region 锚点位置计算：
            switch (anchorType)
            {
                //    //--设置锚点为左下角
                //    rectTrans.anchorMin = Vector2.zero;
                //    rectTrans.anchorMax = Vector2.zero;
                //    rectTrans.pivot = Vector2.zero;

                //    Vector2 pos = Vector2.zero, size = Vector2.zero;

                //    //--获取画布大小：
                //    Vector2 parentSize = DialogCanvas.canvasResolution;
                //    float scrWidth = parentSize.x, scrHeight = parentSize.y;


                //    //--计算位置：
                //    pos.x = scrWidth * percentPosX;
                //    pos.y = scrHeight * percentPosY;
                //    //--计算大小：
                //    size.x = scrWidth * percentSizeX;
                //    size.y = scrHeight * percentSizeY;

                //    //--设置位置、大小
                //    rectTrans.position = pos;
                //    rectTrans.sizeDelta = size;
                //    break;

                ////case AnchorType.CenterVertHorz:
                ////    //--锚点为中心点
                ////    rectTrans.anchorMin = new Vector2(0.5F, 0.5F);
                ////    rectTrans.anchorMax = new Vector2(0.5F, 0.5F);
                ////    rectTrans.pivot = new Vector2(0.5F, 0.5F);
                ////    break;

                case BaseAnchorType.FillCorner:
                    //锚点为填充父物体型的
                    rectTrans.anchorMin = new Vector2(percentPosX, percentPosY);
                    rectTrans.anchorMax = new Vector2(percentPosX + percentSizeX, percentPosY + percentSizeY);
                    rectTrans.pivot = Vector2.zero;

                    //--SizeDelta保持为0即可，用于设置大小
                    rectTrans.sizeDelta = Vector2.zero;

                    //--设置大小后的：偏移值
                    rectTrans.offsetMin = new Vector2(deltaLeft, deltaDown);
                    rectTrans.offsetMax = new Vector2(deltaRight, deltaUp);
                    break;
            }
            #endregion
        }

        /// <summary>
        /// 设置UI可拖动
        /// </summary>
        public static void SetUIDragable(RectTransform rectTrans)
        {
            if (rectTrans.GetComponent<GameUIDragable>() == null)
            {
                rectTrans.gameObject.AddComponent<GameUIDragable>();
            }
        }

        /// <summary>
        /// 设置UI可被点击，并设置其回调函数
        /// </summary>
        public static void SetUIClickable(RectTransform uiRectTrans, CallbackFunc onClick, CallbackFunc onClickeDown = null, CallbackFunc onClickUp = null)
        {
            GameUIClickable clickable = uiRectTrans.GetComponent<GameUIClickable>();
            if (clickable == null)
            {
                clickable = uiRectTrans.gameObject.AddComponent<GameUIClickable>();
            }

            //--设置回调：
            clickable.SetOnClickListener(onClick, onClickeDown, onClickUp);
        }

        /// <summary>
        /// 设置ui可在鼠标/触控进入时监听enter、exit事件
        /// </summary>
        public static void SetUIHoverListener(RectTransform uiObj, CallbackFunc onEnter = null, CallbackFunc onExit = null)
        {
            GameUIEnterExitable exable = uiObj.GetComponent<GameUIEnterExitable>();
            if (exable == null)
                exable = uiObj.gameObject.AddComponent<GameUIEnterExitable>();

            if (onEnter != null)
                exable.SetOnEnterListener(onEnter);

            if (onExit != null)
                exable.SetOnExitListener(onExit);

        }

        /// <summary>
        /// 设置UI可被鼠标进入，且能监听鼠标/触控的Enter事件
        /// </summary>
        /// <seealso cref="SetUIHoverListener"/>
        public static void SetUIEnterListener(RectTransform uiObj, CallbackFunc onEnter)
        {
            SetUIHoverListener(uiObj, onEnter);
        }

        /// <summary>
        /// 设置UI可被鼠标进入，且能监听鼠标/触控的Exit事件
        /// </summary>
        /// <seealso cref="SetUIHoverListener"/>
        public static void SetUIExitListener(RectTransform uiObj, CallbackFunc onExit)
        {
            SetUIHoverListener(uiObj, null, onExit);
        }

        /// <summary>
        /// Destroy RectTransform
        /// </summary>
        public static void DestroyRectTrans(RectTransform trans)
        {
            GameObject.Destroy(trans.gameObject);
        }

        /// <summary>
        /// 删除parent下的所有子节点
        /// </summary>
        /// <param name="startFrom">指定从一个特定位置开始往后删除</param>
        public static void RemoveAllChildren(Transform parent, int startFrom = 0)
        {
            int childCount = parent.childCount;
            for (int i = startFrom; i < childCount; i++)
            {
                GameObject.Destroy(parent.GetChild(i).gameObject);
            }
        }

        #endregion

        #region UI 编辑器扩展

#if UNITY_EDITOR

        //热键：%-CTRL #Shift & ALT
        [UnityEditor.MenuItem("GameObject/UI/AddPanelFillH %#h", false, 14)]
        /// <summary>
        /// 添加一个水平填充的Panel
        /// </summary>
        private static void AddPanelFillH()
        {
            RectTransform newOne = AddPanelFill(UnityEditor.Selection.activeGameObject.transform,
                LayoutDirection.Horizontal);

            //向编辑器注册物体的生成：
            UnityEditor.Undo.RegisterCreatedObjectUndo(newOne.gameObject, "panel obj");
        }

        /// <summary>
        /// 添加一个垂直布局的子Panel
        /// </summary>
        [UnityEditor.MenuItem("GameObject/UI/AddPanelFillV %#v", false, 15)]
        private static void AddPanelFillV()
        {
            RectTransform newOne = AddPanelFill(UnityEditor.Selection.activeGameObject.transform,
                LayoutDirection.Vertical);
            UnityEditor.Undo.RegisterCreatedObjectUndo(newOne.gameObject, "panel obj");
        }

        /// <summary>
        /// 将子节点全部转换为垂直布局
        /// </summary>
        [UnityEditor.MenuItem("GameObject/UI/Edit/MakeFill_V %#&v", false)]
        private static void ChangeChildrenToFillPanelV()
        {
            GameObject currObj = UnityEditor.Selection.activeGameObject;
            ConvertChildrenPanelLayout(currObj.transform, LayoutDirection.Vertical);
        }

        /// <summary>
        /// 将子节点全部转为水平布局
        /// </summary>
        [UnityEditor.MenuItem("GameObject/UI/Edit/MakeFill_H %#&h", false)]
        private static void ChangeChildrenToFillPanelH()
        {
            GameObject currObj = UnityEditor.Selection.activeGameObject;
            ConvertChildrenPanelLayout(currObj.transform, LayoutDirection.Horizontal);
        }
#endif

        //------------------------------------------------------------
        //                      辅助函数
        //------------------------------------------------------------
        /// <summary>
        /// 布局方向，私有
        /// </summary>
        enum LayoutDirection
        {
            Horizontal,
            Vertical,
        }

        /// <summary>
        /// 根据参数生成自动填充布局属性的panel,并返回新生成的panel
        /// </summary>
        /// <param name="isHorizPanel">是否是水平布局的panel</param>
        private static RectTransform AddPanelFill(Transform parentTrans, LayoutDirection layoutDirection)
        {
            int childCount = parentTrans.childCount;

            //生成一个子物体

            RectTransform rtChildCreated = CreatePanel("Panel_" + childCount);

            //--添加入父物体中：
            AttachRectTrans(rtChildCreated, parentTrans);
            childCount++;

            //开始转换所有子物体的布局
            ConvertChildrenPanelLayout(parentTrans, layoutDirection);



            //返回新生成的物体
            return rtChildCreated;
        }

        private static void ConvertChildrenPanelLayout(Transform parentTrans, LayoutDirection layoutDirection)
        {
            int childCount = parentTrans.childCount;
            if (childCount <= 0)
                return;

            #region 开始遍历所有子物体，并重新设置横向占据的百分比            
            RectTransform currChildTran;
            //--计算panel 占据的百分比%，用int方便控制小数位数为2；
            int sizeDeltaRange = 100 / childCount;
            int currSizeStart = 0;                  //当前占据值起始点

            //--如果是竖行排版，则start和delta方式为逆方式：
            #region 设置vertical排版时的delta和起始位置：
            if (layoutDirection == LayoutDirection.Vertical)
            {
                sizeDeltaRange = -sizeDeltaRange;
                currSizeStart = 100;
            }
            #endregion

            for (int i = 0; i < childCount; i++)
            {
                currChildTran = parentTrans.GetChild(i).GetComponent<RectTransform>();

                //--现在anchor全部采取小数表示
                float startAnchor = currSizeStart * 0.01f;
                float endAnchor = (currSizeStart + sizeDeltaRange) * 0.01f;
                if (i == childCount - 1)
                {
                    //--最后一个anchor计算的时候，终点anchor直接为1/0
                    if (sizeDeltaRange < 0)
                        endAnchor = 0f;                 //负值增长0
                    else
                        endAnchor = 1;                  //正到1
                }

                //根据布局方向不同，设置x/y
                switch (layoutDirection)
                {
                    case LayoutDirection.Horizontal:
                        currChildTran.anchorMin = new Vector2(startAnchor, 0f);
                        currChildTran.anchorMax = new Vector2(endAnchor, 1f);
                        break;

                    case LayoutDirection.Vertical://竖直排版时需要max和min的y值颠倒，因为布局是从下往上的，而坐标相反：
                        currChildTran.anchorMin = new Vector2(0f, endAnchor);
                        currChildTran.anchorMax = new Vector2(1f, startAnchor);

                        break;
                }

                currChildTran.anchoredPosition = Vector2.zero;
                //--内部间距设置0：如果需要调整则为-10，-10
                currChildTran.sizeDelta = new Vector2(0, 0);

                //--起始点自增，或不设置
                currSizeStart += sizeDeltaRange;
            }
            #endregion

        }

#if UNITY_EDITOR
        //填充的验证项：只允许在ui物体上添加
        [UnityEditor.MenuItem("GameObject/UI/AddPanelFillH %#h", true)]
        [UnityEditor.MenuItem("GameObject/UI/AddPanelFillV %#v", true)]
        [UnityEditor.MenuItem("GameObject/UI/Edit/MakeFill_V %#&v", true)]
        [UnityEditor.MenuItem("GameObject/UI/Edit/MakeFill_H %#&h", true)]
        private static bool UIObjValidation()
        {
            return UnityEditor.Selection.activeGameObject.GetComponent<RectTransform>() != null;
        }
#endif

        #endregion

        #region 静态效果区域
        /// <summary>
        /// 显示这个UI
        /// </summary>
        [Obsolete("USE: CoroutineHelper.StartCoroutineUsing(trans) & show obj by yourself;")]
        internal static void ShowUI(RectTransform uiRect)
        {
            CrtShowUI(uiRect);
        }

        /// <summary>
        /// 尝试关闭这个UI（如果还有其他协程占用，则将保持显示直到所有协程都尝试了关闭UI）
        /// </summary>
        [Obsolete("USE: CoroutineHelper.TryEndCoroutineFor(trans);")]
        internal static void TryCloseUI(RectTransform uiRect)
        {
            CrtCloseUI(uiRect);
        }

        /// <summary>
        /// 显示这个UI，在多协程使用同一个UI时可以有效管理开关
        /// </summary>
        [Obsolete("USE: CoroutineHelper.StartCoroutineUsing(trans) & show obj by yourself;")]
        internal static void CrtShowUI(RectTransform uiRect)
        {
            CoroutineHelper.StartCoroutineUsing(uiRect);

            //--显示这个UI
            uiRect.gameObject.SetActive(true);

        }

        /// <summary>
        /// 尝试关闭这个UI，在多协程使用同一个UI时可以有效管理开关（如果还有其他协程占用，则将保持显示直到所有协程都尝试了关闭UI）
        /// </summary>
        [Obsolete("USE: CoroutineHelper.TryEndCoroutineFor(trans);")]
        internal static void CrtCloseUI(RectTransform uiRect, bool removeObjHolder = false)
        {
            CoroutineHelper.TryEndCoroutineFor(uiRect, () => { uiRect.gameObject.SetActive(false); }, removeObjHolder);

        }

        /// <summary>
        /// 探查这个UI是否显示ing，在多协程使用同一个UI时可以有效管理
        /// </summary>
        [Obsolete("USE: CoroutineHelper.IsUIUsing(trans);")]
        internal static bool IsUIShowing(RectTransform uiRect)
        {
            return CoroutineHelper.IsUIUsing(uiRect);
        }
        //--         End of UI show & hide

        public static void ChangeUILayoutElment(UIBehaviour uiObj, int height)
        {
            LayoutElement element = uiObj.GetComponent<LayoutElement>();
            element.minHeight = element.preferredHeight = height;

        }

        public enum UIEffectType { OutLine, Shadow };
        public static void AddEffect(GameObject srcObj, UIEffectType effectType)
        {
            Color effColor = Color.black;
            effColor.a = 0.5f;
            Vector2 effectDistance = new Vector2(1, -1);
            AddEffect(srcObj, effectType, effColor, effectDistance);
        }

        public static void AddEffect(GameObject srcObj, UIEffectType effectType, Color effectColor, Vector2 effectDistance)
        {
            Shadow eff = null;
            switch (effectType)
            {
                case UIEffectType.OutLine:
                    //--添加OutLine效果：
                    eff = srcObj.AddComponent<Outline>();

                    break;

                case UIEffectType.Shadow:
                    //--添加Shadow效果
                    eff = srcObj.AddComponent<Shadow>();
                    break;
            }

            //--设置对应属性：
            eff.effectColor = effectColor;
            eff.effectDistance = effectDistance;
        }

        #endregion

        #region UI动态效果区域

        public static void PrintText(Text textToPrint, CallbackFunc onPrintOver = null)
        {
            //Start a corotine for text:
            textToPrint.StartCoroutine(IE_PrintText(textToPrint, textToPrint.text, 0.1F, true, false, onPrintOver));
        }

        public static void PrintText(Text textToPrint, string text, float charJumpTime = 0.1f, bool clickToBreak = true, bool waitClickWhenPrintOver = false, CallbackFunc onPrintOver = null)
        {
            //Start a corotine for text:
            textToPrint.StartCoroutine(IE_PrintText(textToPrint, text, charJumpTime, clickToBreak, waitClickWhenPrintOver, onPrintOver));
        }

        /// <summary>
        /// 直接让UI定位到一个场景中的游戏物体身上
        /// </summary>
        /// <param name="deltaYToTarget">与目标物体Transform在Y轴上的偏移值</param>
        public static void FollowTrans(RectTransform UITrans, Transform target, float deltaYToTarget = 0)
        {
            //Follow position:
            Vector3 tmpV3 = Camera.main.WorldToScreenPoint(target.position);
            tmpV3.y -= deltaYToTarget;
            UITrans.position = tmpV3;
        }


        ///// <summary>
        ///// 直接让UI定位到一个场景中的World position
        ///// </summary>
        ///// <param name="deltaYToTarget">与目标物体Transform在Y轴上的偏移值</param>
        //public static void FollowPos(RectTransform UITrans, Vector3 worldPos, float deltaYToTarget = 0)
        //{
        //    //Follow position:
        //    Vector3 tmpV3 = Camera.main.WorldToScreenPoint(worldPos);
        //    tmpV3.y -= deltaYToTarget;
        //    UITrans.position = tmpV3;
        //}

        public static void SetUIFreeze(RectTransform btnSpecial, float freezeTime, CallbackFunc onUIUnfreezed)
        {
            var imgComp = btnSpecial.GetComponent<Image>();
            if (imgComp != null)
            {
                if (imgComp.type != Image.Type.Filled)
                {
                    imgComp.type = Image.Type.Filled;
                    imgComp.fillMethod = Image.FillMethod.Radial360;
                }

                float timePassed = 0;
                imgComp.fillAmount = 0;
                imgComp.StartCoroutine(GameObjFunc.IUpdateDo(freezeTime,
                    () =>  //update action:
                    {
                        imgComp.fillAmount = Mathf.Clamp01(timePassed / freezeTime);
                        timePassed += Time.deltaTime;
                    },

                    //On done:
                    () =>
                    {
                        imgComp.fillAmount = 1;
                        if (onUIUnfreezed != null)
                        {
                            onUIUnfreezed();
                        }
                    }));

            }
            else
            {
                Debug.LogWarning("No image component found, cannot freeze it.");
            }
        }


        /// <summary>
        /// 在一定时间内，移动一个UI到一个指定的场景物体的Transform上：比如移动一张Image到一个游戏物体上。
        /// </summary>
        public static IEnumerator IUIMoveToTransform(RectTransform trans, Transform transTarget, Vector3 moveFrom, float deltaYToTarget = 0, bool fallBack = true, float moveTime = 1.5F)
        {
            Vector3 targetPos;
            Vector3 fromPos;
            Vector3 currPos = trans.position;
            float timePass = 0;
            while (timePass < moveTime)
            {
                targetPos = Camera.main.WorldToScreenPoint(transTarget.position);
                fromPos = trans.position;

                currPos.x = Mathf.Lerp(fromPos.x, targetPos.x, timePass);
                currPos.y = Mathf.Lerp(fromPos.y, targetPos.y, timePass) - deltaYToTarget;

                trans.position = currPos;

                timePass += Time.deltaTime;
                yield return 1;
            }

            if (fallBack)
            {
                //Reset back:
                trans.position = moveFrom;
            }
        }

        /// <summary>
        /// 在一定时间内，把一个UI移动到目标V3的位置
        /// </summary>
        /// <param name="onDone">当完成移动时的回调</param>
        /// <param name="srcRT">源Transform</param>
        /// <param name="targetPos">目标位置</param>
        /// <param name="moveTime">移动时间（秒）</param>
        public static IEnumerator IMoveUIToRectTransPos(RectTransform srcRT, Vector3 targetPos, CallbackFunc onDone = null, float moveTime = 1f)
        {
            float timePassed = 0F;
            Vector3 v3Src = srcRT.position;
            float percentage = 0f;                      //passed的时间与指定的时间的百分比

            if (moveTime > 0)                           //避免除数为0，或者负
            {
                while (percentage <= 1)
                {
                    srcRT.position = Vector3.Lerp(v3Src, targetPos, percentage);

                    timePassed += Time.deltaTime;
                    percentage = timePassed / moveTime;
                    yield return 1;
                }
            }

            srcRT.position = targetPos;

            if (onDone != null)
            {
                onDone();
            }
        }

        /// <summary>
        /// 在一定时间内，让UI绕着Y轴旋转
        /// </summary>
        public static IEnumerator IRotateUI_Y(RectTransform rectToDo, float speed = 180, float rotateTime = 2)
        {
            while (rotateTime > 0)
            {
                rotateTime -= Time.deltaTime;
                rectToDo.Rotate(0, speed * Time.deltaTime, 0);
                yield return 1;
            }

            rectToDo.rotation = Quaternion.identity;
        }

        /// <summary>
        /// 在一定时间内，将物体平滑缩放到指定大小
        /// </summary>
        public static IEnumerator IScaleUITo(RectTransform rectToDo, Vector3 toSize, float scaleTime = 1)
        {
            Vector3 currSize = rectToDo.localScale;

            //获得目标与当前的每秒间差值，即可通过deltaTime控制缩放速度
            Vector3 deltaPerSecond = (toSize - currSize) / scaleTime;
            while (scaleTime > 0)
            {
                scaleTime -= Time.deltaTime;
                currSize = currSize + deltaPerSecond * Time.deltaTime;
                rectToDo.localScale = currSize;
                yield return 1;
            }

            rectToDo.localScale = toSize;
        }

        /// <summary>
        /// 平滑地滑动Slider到指定值
        /// 此处未控制多个协程的情况，需要调用方决定
        /// </summary>
        /// <param name="callBackWaitTime">如果Slider的值已经滑动完成，可以指定再更新一会儿才调用完成回调If slider is finished,then wait for a few time</param>
        public static IEnumerator IDoSliderBlinkTo(Slider slider, float slideTo, float blinkTime = 0.5F, CallbackFunc UpdateAction = null, CallbackFunc CallbackAction = null, float callBackWaitTime = 0)
        {
            Image fillImage = slider.GetComponentsInChildren<Image>()[1];
            Color preColor = fillImage.color;
            float beforeValue = slider.value;

            Color currColor = slideTo > beforeValue ?
                Color.white :                                            //UP Color
                Color.grey;                                            //Down color


            float blinkTimeRevert = 1F / blinkTime;
            float imgAlpha = fillImage.color.a;
            while (blinkTime >= 0)
            {
                slider.value += (slideTo - beforeValue) * Time.deltaTime * blinkTimeRevert;
                currColor += (preColor - currColor) * Time.deltaTime;
                currColor.a = imgAlpha;
                fillImage.color = currColor;

                if (UpdateAction != null)
                    UpdateAction();

                blinkTime -= Time.deltaTime;
                yield return 1;
            }

            slider.value = slideTo;
            fillImage.color = preColor;
            while (callBackWaitTime > 0)
            {
                if (UpdateAction != null)
                    UpdateAction();

                callBackWaitTime -= Time.deltaTime;
                yield return 1;
            }

            if (CallbackAction != null)
                CallbackAction();
        }

        /// <summary>
        /// 让UI上下跳动一次
        /// </summary>
        /// <param name="jumpCount">跳动的次数，默认上下跳动一次。</param>
        /// <param name="callBack">跳完回调</param>
        /// <param name="rectToDo">ui</param>
        /// <param name="jumpSpeed">跳动速度</param>
        /// <param name="pingPongJump">是否以UI为原点上下跳动；否则只向上跳动；</param>
        /// <param name="backToPivot">是否记录原点，并且在跳动完后回到原点</param>
        /// <param name="jumpHeightScaleOfScreen">跳动高度，相对于屏幕而言</param>
        public static IEnumerator IDoJumpUI(RectTransform rectToDo, bool backToPivot = true, int jumpCount = 1, float jumpSpeed = 5, CallbackFunc callBack = null, float jumpHeightScaleOfScreen = 0.0047F)
        {
            return IDoJumpUI(rectToDo, backToPivot, jumpCount, jumpSpeed, true, callBack, jumpHeightScaleOfScreen);
        }


        /// <summary>
        /// 让UI向上跳动一次
        /// </summary>
        /// <param name="jumpCount">跳动的次数，默认上下跳动一次。</param>
        /// <param name="callBack">跳完回调</param>
        /// <param name="rectToDo">ui</param>
        /// <param name="jumpSpeed">跳动速度</param>
        /// <param name="backToPivot">是否记录原点，并且在跳动完后回到原点</param>
        /// <param name="jumpHeightScaleOfScreen">跳动高度，相对于屏幕而言</param>
        public static IEnumerator IDoJumpUpUI(RectTransform rectToDo, bool backToPivot = true, int jumpCount = 1, float jumpSpeed = 5, CallbackFunc callBack = null, float jumpHeightScaleOfScreen = 0.0047F)
        {
            return IDoJumpUI(rectToDo, backToPivot, jumpCount, jumpSpeed, false, callBack, jumpHeightScaleOfScreen);
        }

        /// <summary>
        /// 让UI上下跳动一次
        /// </summary>
        /// <param name="jumpCount">跳动的次数，默认上下跳动一次。</param>
        /// <param name="callBack">跳完回调</param>
        /// <param name="rectToDo">ui</param>
        /// <param name="jumpSpeed">跳动速度</param>
        /// <param name="pingPongJump">是否以UI为原点上下跳动；否则只向上跳动；</param>
        /// <param name="backToPivot">是否记录原点，并且在跳动完后回到原点</param>
        private static IEnumerator IDoJumpUI(RectTransform rectToDo, bool backToPivot = true, int jumpCount = 1, float jumpSpeed = 5, bool pingPongJump = true, CallbackFunc callBack = null, float jumpHeightScaleOfScreen = 0.0047F)
        {
            float doingTime = 0;
            Vector3 preLocalPos = rectToDo.localPosition;                   //记录相对位置而不是绝对位置
            Vector3 currPos = preLocalPos;

            float totalTimeToDo = 6.2831853F * jumpCount;               //2π=6.28..., 一个周期完毕。指定多少周期就多大咯
            float jumpHighest = jumpHeightScaleOfScreen * Screen.height;
            float deltaPos;

            CoroutineHelper.ObjHolder objHolder = null;
            if (backToPivot)
            {
                objHolder = CoroutineHelper.FindObjHolder(rectToDo, true);
                if (objHolder.otherData == null)
                {
                    //可认定为第一次的调用者，此时记录transform的位置：
                    objHolder.otherData = rectToDo.localPosition;
                }
            }

            //--Do UI part
            do
            {
                //原：rectToDo.Translate(0, Mathf.Sin(doingTime) * jumpHighest, 0);
                //使用积分的方式：对sin（x）从0~t积分，有：∫sin（x）=-cost（t）+cos（0） = -cos（t）+1
                //对于积分形式，会上弹，然后回到原位；对于直接使用sin，会以原位置为中心上下弹一次；
                //即：积分形式会上移2，而sin会上下移动1、-1
                if (pingPongJump)
                {
                    deltaPos = Mathf.Sin(doingTime);
                }
                else
                {
                    deltaPos = -Mathf.Cos(doingTime) + 1; //其实就是Cos函数翻转后上移
                }

                currPos.y = preLocalPos.y + deltaPos * jumpHighest;
                rectToDo.localPosition = currPos;

                doingTime += (Time.deltaTime * jumpSpeed);

                yield return 1;                                             //Wait For one frame
            } while (doingTime < totalTimeToDo);

            if (backToPivot)
            {
                //使用第一次调用协程时记录的位置
                rectToDo.localPosition = (Vector3)objHolder.otherData;
            }

            if (callBack != null)
            {
                callBack();
            }
        }

        /// <summary>
        /// 对任一拥有透明度的UI组件进行渐变
        /// </summary>
        /// <param name="time">渐变使用的时间</param>
        /// <param name="fadeIn">是否渐入，否则渐出</param>
        public static IEnumerator IFadeTween(Graphic compWithAlpha, bool fadeIn, float time)
        {
            //--获得颜色：
            Color currColor = compWithAlpha.color;

            //--计算从0到1的颜色渐变的速度：（颜色从0~1分布）
            float fadeSpeed = 1F / time;

            //--如果是渐出，则从1到0
            if (fadeIn == false)
            {
                fadeSpeed = -1f * fadeSpeed;
                currColor.a = 1;
            }
            else
            {
                //--渐入，则从0到1 
                currColor.a = 0;
            }

            while (time > 0)
            {
                time -= Time.deltaTime;

                //--计算此时的alpha
                currColor.a += fadeSpeed * Time.deltaTime;

                //--设置：
                compWithAlpha.color = currColor;

                //--每帧循环
                yield return 1;
            }

            yield return null;
        }

        /// <summary>
        /// 跳出不断地更新某行文字的效果，如目标值
        /// </summary>
        /// <param name="srcText"></param>
        /// <param name="textFormat"></param>
        /// <param name="startCount">当前该数值计数显示</param>
        /// <param name="targetCount">即将跳动到哪些数值展示</param>
        /// <returns></returns>
        public static void FadeTweenCounterText(Text srcText, string textFormat, int startCount, int targetCount)
        {
            Transform textTrans = srcText.transform;
            var objHolder = CoroutineHelper.FindObjHolder(textTrans, true);

            int increaseNum = (targetCount >= startCount ? 1 : -1);
            if (objHolder.IsObjUsing())
            {
                //已经有协程在使用，则跳过：
                objHolder.otherData = targetCount;
            }
            else
            {
                int currCount = startCount;
                float timePsd = 0;

                objHolder.AddObjUseCount();

                BoolCallbackFunc updateContinueCb = () => currCount < targetCount;
                if (increaseNum < 0)
                {
                    updateContinueCb = () => currCount > targetCount;
                }



                //可以开启一个协程更新：
                srcText.StartCoroutine(GameObjFunc.IUpdateDo(() =>
                {
                    //startCount+= increaseNum;

                    currCount = (int)(Mathf.Lerp(startCount, targetCount, timePsd));
                    timePsd += Time.deltaTime;

                    srcText.text = string.Format(textFormat, currCount);

                    if (objHolder.otherData != null)
                    {
                        targetCount = (int)objHolder.otherData;//需要更新目标值，避免中途被人修改了
                    }
                }, 0,                         //更新文字频率
                updateContinueCb, //当当前数量还没达到目标数量时，则继续
                () =>
                {
                    //完成后，文字更新，协程标记不再使用：
                    objHolder.SubObjUseCount();
                    srcText.text = string.Format(textFormat, targetCount);//不可忽略（如果数值是变成更小的呢……）

                    CoroutineHelper.RemoveObjHolder(objHolder);
                }
                ));

            }
        }

        /// <summary>
        /// 不断地打出文字效果（可决定是否点击鼠标打断，唯一问题是无换页）
        /// </summary>
        /// <param name="charDelayTime"></param>
        /// <param name="waitClickWhenPrintOver">当传入OnPrintOver的时候，是否只在点击的时候才判断完全结束</param>
        private static IEnumerator IE_PrintText(Text srcText, string textStr, float charDelayTime, bool clickBreak, bool waitClickWhenPrintOver, CallbackFunc onPrintOver)
        {
            if (string.IsNullOrEmpty(textStr))
            {
                srcText.text = textStr;
                yield return -1;
            }
            srcText.text = "";

            int count = textStr.Length;

            #region version 1
            float timePsd = 0;
            int currWord = 1;

            while (true)
            {
                yield return 1;
                timePsd += Time.deltaTime;

                //TODO:Input时进行Break：
                if (clickBreak)
                {
                    if (MyInput.GetMouseDown())
                    {
                        currWord = count;
                    }
                }

                if (timePsd > charDelayTime)
                {
                    timePsd -= charDelayTime;

                    //开始Sub 字符 
                    if (currWord <= count)
                    {
                        srcText.text = textStr.Substring(0, currWord);   //<<<这里可以优化优化，何必sub那么多？

                        currWord++;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            #endregion

            #region verson 0
            //获得每个字符的延迟
            //WaitForSeconds eachCharWaitTime = new WaitForSeconds(charDelay);

            ////Show it one by one:
            //for (int i = 1; i <= count; i++)
            //{
            //    srcText.text = textStr.Substring(0, i);

            //    //wait for a while
            //    yield return eachCharWaitTime;
            //}
            #endregion

            //Print over:
            if (onPrintOver != null)
            {
                if (waitClickWhenPrintOver)
                {
                    while (true)
                    {
                        if (MyInput.GetMouseDown())
                        {
                            break;
                        }
                        yield return 1;
                    }
                }

                onPrintOver();
            }
        }

        #endregion

        #region UI资源区域
        enum GetImageType { Button, InputField, Knob, DownArrow, CheckMark, UIBackground, UISprite };
        private static Sprite GetImg(GetImageType imgType)
        {
            switch (imgType)
            {
                case GetImageType.Button:
                case GetImageType.UISprite:
                    return GameUISpriteProvider.g_instance.m_UISprite;


                case GetImageType.InputField:
                    return GameUISpriteProvider.g_instance.m_UIInputFieldBG;

                case GetImageType.Knob:
                    return GameUISpriteProvider.g_instance.m_UIKnob;

                case GetImageType.DownArrow:
                    return GameUISpriteProvider.g_instance.m_UIDownArrow;

                case GetImageType.CheckMark:
                    return GameUISpriteProvider.g_instance.m_UICheckMark;

                case GetImageType.UIBackground:
                    return GameUISpriteProvider.g_instance.m_UIBG;
            }

            return GameUISpriteProvider.g_instance.m_UISprite;
        }


        public static GameUISkin GetGameUISkin()
        {
            return GameUISpriteProvider.g_instance.m_gameStyleUI;
        }

        /// <summary>
        /// 获得游戏场景中的主Canvas（无干扰Canvas）
        /// </summary>
        /// <returns></returns>
        public static RectTransform GetMainGameCanvasRectTrans()
        {
            return MainGameCanvas.UIMainGameCanvas;
        }
        #endregion

    }


}